[实例] 轮播图
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>[React] 轮播图</title>
<!-- React 核心库,与宿主环境无关 -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 依赖核心库,将核心功能与页面结合 -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- babel -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
<style>
.banner {
border: 1px solid #ccc;
overflow: hidden;
position: relative;
}
.img-container {
/* border: 1px solid #fac; */
float: left;
}
.dots {
align-items: center;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 15px;
bottom: 30px;
display: flex;
height: 30px;
justify-content: space-around;
left: 50%;
position: absolute;
transform: translateX(-50%);
width: 300px;
z-index: 1;
}
.dots .dot {
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
display: inline-block;
height: 10px;
width: 10px;
}
.dots .dot.active {
background-color: #fac;
}
.dots .dot:not(.active) {
cursor: pointer;
}
</style>
</head>
<body>
<div id="root"></div>
<!-- 图片容器组件 -->
<script type="text/babel">
class ImgContainer extends React.Component {
static propTypes = {
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
imgList: PropTypes.arrayOf(PropTypes.string).isRequired,
duration: PropTypes.number.isRequired,
autoDuration: PropTypes.number.isRequired,
autoPlay: PropTypes.bool.isRequired,
};
static defaultProps = {
width: 800,
height: 600,
imgList: [],
duration: 500,
autoDuration: 500,
autoPlay: true,
};
state = {
index: 0,
marginLeft: 0,
};
autoTimer = null; // 自动播放定时器
tickTimer = null; // 间隔定时器
tick = 16; // 定时器间隔
containerRef = (el) => {
this.container = el;
};
/**
* 切换图片
* @returns
*/
switchImg = (index) => {
// 1. 计算最终index 的marginLeft
const targetMarginLeft = -this.props.width * index;
// 2. 获取当前marginLeft
let currentMarginLeft = +window
.getComputedStyle(this.container)
.marginLeft.split("px")[0];
// 3. 计算运动次数
const times = Math.ceil(this.props.autoDuration / this.tick);
let curTime = 0;
// 4. 移动总距离
const totalDis = targetMarginLeft - currentMarginLeft;
const stepDis = totalDis / times;
//
clearInterval(this.tickTimer);
// 5. 开始移动
this.tickTimer = setInterval(() => {
curTime++;
currentMarginLeft += stepDis;
this.setState({
marginLeft: currentMarginLeft,
});
// 边界
if (curTime === times) {
clearInterval(this.tickTimer);
this.setState({
marginLeft: targetMarginLeft,
});
return;
}
}, this.tick);
};
/**
* 自动播放
*/
autoPlay = () => {
clearInterval(this.autoTimer);
let direction = 1; // 1: 向右, -1: 向左
this.autoTimer = setInterval(() => {
let index = this.state.index;
if (index <= 0) {
direction = 1;
}
if (index >= this.props.imgList.length - 1) {
direction = -1;
}
this.setState({
index: index + direction,
});
this.switchImg(this.state.index);
}, this.props.duration);
};
/**
* 点击dot
*/
handleClickDot = (e) => {
if (e.target.className.includes("active")) {
e.stopPropagation();
}
const index = +e.target.dataset.index;
this.setState({
index,
});
this.switchImg(index);
};
componentDidMount() {
if (this.props.autoPlay) {
this.autoPlay();
}
}
render() {
return (
<>
<div
ref={this.containerRef}
className="img-container"
style={{
width: this.props.width * this.props.imgList.length,
height: this.props.height,
marginLeft: this.state.marginLeft + "px",
}}
>
{this.props.imgList.map((item, index) => (
<img
key={index}
src={item}
alt=""
style={{
width: this.props.width,
height: this.props.height,
}}
/>
))}
</div>
<div
className="dots"
style={{ width: 50 * this.props.imgList.length + "px" }}
>
{this.props.imgList.map((item, index) => (
<span
key={index}
data-index={index}
className={
this.state.index === index ? "dot active" : "dot"
}
onClick={this.handleClickDot}
></span>
))}
</div>
</>
);
}
}
</script>
<!-- 轮播图组件 -->
<script type="text/babel">
class Banner extends React.Component {
//
static propTypes = {
//
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
duration: PropTypes.number.isRequired,
autoDuration: PropTypes.number.isRequired,
autoPlay: PropTypes.bool.isRequired,
imgList: PropTypes.arrayOf(PropTypes.string).isRequired,
};
static defaultProps = {
width: 800,
height: 600,
duration: 3000,
autoDuration: 500,
autoPlay: true,
imgList: [
"https://fastly.picsum.photos/id/25/800/600.jpg?hmac=c3XP15iyQtTasbi_voCiOB1nHCq0O-mET-ccc6UZgqI",
"https://fastly.picsum.photos/id/28/800/600.jpg?hmac=XlKXM-KYpKBEaXsPpMCtXiI1-YlgaN5DDmAAhbFE5h8",
"https://fastly.picsum.photos/id/29/800/600.jpg?hmac=KbDC2qhBvoFM4XpOZrAnybO4JNiXS9mox0PORy6NCJA",
"https://fastly.picsum.photos/id/33/800/600.jpg?hmac=wsfu4HjxsdEud_jpRFwK2l0D-PTjHAJES3RyYvGHHHU",
],
};
render() {
return (
<div
className="banner"
style={{ width: this.props.width, height: this.props.height }}
>
<ImgContainer
width={this.props.width}
height={this.props.height}
imgList={this.props.imgList}
duration={this.props.duration}
autoDuration={this.props.autoDuration}
autoPlay={this.props.autoPlay}
/>
</div>
);
}
}
</script>
<script type="text/babel">
ReactDOM.render(<Banner />, document.getElementById("root"));
</script>
</body>
</html>